<?php

namespace common\components;

use common\components\helpers\PublicHelper;
use common\service\FormInfoService;
use common\service\PubService;
use common\service\RequestUtilService;
use Yii;
use yii\log\Logger;

require_once __DIR__ . "/alipay/alipay_submit.class.php";
require_once __DIR__ . "/alipay/alipay_notify.class.php";


class AliPay
{


    public static $partner;
    public static $sellerEmail;
    public static $key;
    public static $service;
    public static $isMobile = false;
    public static $alipayId = 1;

    // 新建支付订单
    public static function addOrder($orderId, $name, $price, $endTime = 0, $notifyUrl = '', $returnUrl = '',$exhibition_id = '')
    {
        self::getAlipayParams($orderId,$exhibition_id);
        $alipaySubmit = self::getAlipaySubmit($notifyUrl, $returnUrl,self::$alipayId);
        $param = self::buildParam($orderId, $name, $price, $endTime, $notifyUrl, $returnUrl);
        return $alipaySubmit->alipay_gateway_new . $alipaySubmit->buildRequestParaToString($param);
    }

    private static function buildParam($orderId, $name, $price, $endTime = 0, $notifyUrl = '', $returnUrl = '')
    {


        // 设置必填信息
        $parameter = [
            'out_trade_no' => $orderId,
            'subject' => $name,
            'total_fee' => ceil($price * 100) / 100,
            'partner' => self::$partner
        ];

        //手机端支付参数不一样

        if (\Yii::$app->isMobile->isMobile()) {
            $parameter['seller_id'] = self::$sellerEmail;
            $parameter['service'] = 'alipay.wap.create.direct.pay.by.user';//wap

        } else {
            $parameter['seller_email'] = self::$sellerEmail;
            $parameter['service'] = 'create_direct_pay_by_user';//pc
        }
        // 选填参数映射表
        $map = array(
            'detail' => 'body',
            'url' => 'show_url',
        );

        if ($endTime > 0) {
            $parameter['it_b_pay'] = date('Y-m-d H:i:s', $endTime);
        }

        if (empty($notifyUrl)) {
            $notifyUrl = \Yii::$app->request->hostInfo . '/pay/notify';
        }
        if (empty($returnUrl)) {
            $returnUrl = \Yii::$app->request->hostInfo.'/pay/result';
        }

        $parameter += [

            '_input_charset' => 'utf-8',
            'payment_type' => '1',
            'notify_url' => $notifyUrl,
            'return_url' =>  $returnUrl
        ];


        return $parameter;

    }

    /**
     * 获取支付宝提交对象
     *
     * @return \AlipaySubmit
     */
    private static function getAlipaySubmit($notifyUrl = '', $returnUrl = '',$alipayId)
    {
        $libraryPath = __DIR__ . '/alipay';
        if (empty($notifyUrl)) {
            $notifyUrl = '/pay/notify';
        }
        if (empty($returnUrl)) {
            $returnUrl = \Yii::$app->request->hostInfo.'/pay/result';
        }

        $config = array(

            'partner' => self::$partner,
            'private_key_path' => $libraryPath . '/key/rsa_private_key.pem',
            'ali_public_key_path' => $libraryPath . '/key/alipay_public_key.pem',
            'sign_type' => 'RSA',
            //'sign_type' => 'MD5',
            'input_charset' => 'utf-8',
            'cacert' => $libraryPath . '/key/cacert.pem',
            'transport' => 'http',
            'notify_url' => \Yii::$app->request->hostInfo . $notifyUrl,
            'return_url' => $returnUrl
        );
        //RSA
        if($alipayId!=1){
            $config['private_key_path']= $libraryPath . '/key/'.$alipayId.'/rsa_private_key.pem';
            $config['ali_public_key_path'] = $libraryPath . '/key/'.$alipayId.'/alipay_public_key.pem';
        }
        //手机端支付参数不一样
        if (self::$isMobile) {
            $config['seller_id'] = self::$sellerEmail;
        } else {
            $config['seller_email'] = self::$sellerEmail;

        }
        return new \AlipaySubmit($config);
    }
    public static function signVerify($parameter, $sign) {
        //除去待签名参数数组中的空值和签名参数
        $para_filter = paraFilter($parameter);

        //对待签名参数数组排序
        $para_sort = argSort($para_filter);

        //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = createLinkstring($para_sort);
        Yii::getLogger()->log('RefundNotify-待签名参数:' . $prestr, Logger::LEVEL_INFO);

        return rsaVerify($prestr, __DIR__ . '/alipay/key/alipay_public_key.pem', $sign);
    }

    /**
     * 检查通知合法性
     *
     * @param  string $notifyId
     * @return bool
     */
    public static function checkNotify($notifyId)
    {

        $url = 'https://mapi.alipay.com/gateway.do';

        $result = $notifyId ? $result = RequestUtilService::request($url, [
            'service' => 'notify_verify',
            'partner' => self::$partner,
            'notify_id' => $notifyId // 此处 !不能! 用urlencode
        ]) : false;

        return $result;
    }

    /**
     * 数据库中取支付宝参数
     * @param $orderId
     */
    public static function getAlipayParams($orderId,$exhibition_id = '')
    {
        if (!empty($exhibition_id)) {
            $orderPayInfo = PubService::getExhibitionDataById($exhibition_id);
            if (empty($orderPayInfo)) {
                echo '获取支付宝配置信息失败！';
                exit;
            }
        } else {
            $orderPayInfo = FormInfoService::getUserInfoByUuid($orderId);
            if (empty($orderPayInfo)) {
                echo '获取订单信息失败！';
                exit;
            }
        }
        $alipay = \common\models\Alipay::findOne($orderPayInfo['alipay_id']);
        self::$partner = $alipay->alipay_partner;
        self::$sellerEmail = $alipay->alipay_seller_email;
        self::$key = $alipay->alipay_key;
        self::$alipayId = $alipay->id;
        self::$isMobile = isset($_SESSION['isMobile']) ? true : false;
    }

    // 新建退款
    public static function refund(array $orders) {
        $details = "";
        $refundReason = '退款';
        $batchNum = count($orders);
        foreach ($orders as $order) {
            if (!isset($order['order_id']) || !isset($order['amount']) || !isset($order['trade_no'])) {
                return ['success' => false, 'message' => 'Invalid orders.'];
            }
            $serialsNumber = $order['trade_no'];
            $amount = $order['amount'] / 100;
            $details .= self::createRefundDetail($serialsNumber, $amount, $refundReason);
        }
        //去掉最后一个#字符
        $detailData = substr($details, 0, count($details) - 2);

        return self::postRefund($batchNum, $detailData);
    }
    private static function createRefundDetail($serialsNumber, $amount, $refundReason) {
        $arg = "";
        $arg .= $serialsNumber . "^" . $amount . "^" . $refundReason . "#";
        return $arg;
    }
    public static function postRefund($batchNum, $detailData, $notifyUrl = '') {
        if (empty($notifyUrl)) {
            $notifyUrl = '/pay/refund-notify';
        }
        /**
         * service 接口
         * "refund_fastpay_by_platform_pwd","refund_fastpay_by_platform_nopwd"
         */
        $parameter = array(
            "service" => "refund_fastpay_by_platform_nopwd",
            "partner" => \Yii::$app->params['alipay.partner'],
            "notify_url" => \Yii::$app->request->hostInfo . $notifyUrl,
            "batch_no" => date('Ymd') .PublicHelper::getNonceStr(24),
            "refund_date" => date('Y-m-d H:i:s', time()),
            "batch_num" => $batchNum,
            "detail_data" => $detailData,
            "_input_charset" => Yii::$app->params['alipay.charset']
        );
        $signParameter = self::getSignParameter($parameter);
        $createUrl =  self::createUrl($signParameter);
        return  self::getHttpResponseGET($createUrl);

    }
    private static function createUrl($signParameter) {
        $url = 'https://mapi.alipay.com/gateway.do?';
        foreach ($signParameter as $key => $value) {
            $url .= $key . '=' . urlencode($value) . '&';
        }
        $url = substr($url, 0, count($url) - 2);
        return $url;

    }

    /**
     * 远程获取数据，GET模式
     * 注意：
     * 1.使用Crul需要修改服务器中php.ini文件的设置，找到php_curl.dll去掉前面的";"就行了
     * 2.文件夹中cacert.pem是SSL证书请保证其路径有效，目前默认路径是：getcwd().'\\cacert.pem'
     * @param $url 指定URL完整路径地址
     * @param $cacert_url 指定当前工作目录绝对路径
     * return 远程输出的数据
     */
    private static function getHttpResponseGET($url,$cacert_url='') {
        $curl = curl_init($url);
        curl_setopt($curl, CURLOPT_HEADER, 0 ); // 过滤HTTP头
        curl_setopt($curl,CURLOPT_RETURNTRANSFER, 1);// 显示输出结果
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);//SSL证书认证
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);//严格认证
        if(!empty($cacert_url)){
            curl_setopt($curl, CURLOPT_CAINFO,$cacert_url);//证书地址
        }
        $responseText = curl_exec($curl);
        //var_dump( curl_error($curl) );//如果执行curl过程中出现异常，可打开此开关，以便查看异常内容
        curl_close($curl);

        return $responseText;
    }
    public static function parseRefundDetail($detailData) {
        $parseDetails = array();
        $details = explode('#', $detailData);
        foreach ($details as $detail) {
            $ret = explode('^', $detail);
            if ($ret[2] !== 'SUCCESS' && !strstr($ret[2], '$$')) {
                return ['success' => false, 'message' => 'detail_data invalid.'];
            }
            $parseDetails[$ret[0]] = $ret[1];
        }
        return ['success' => true, 'data' => $parseDetails];
    }
    private static function getSignParameter($parameter) {
        //除去待签名参数数组中的空值和签名参数
        $para_filter = paraFilter($parameter);

        //对待签名参数数组排序
        $para_sort = argSort($para_filter);

        //把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        $prestr = createLinkstring($para_sort);

        //签名
         $sign = rsaSign($prestr, __DIR__ . '/alipay/key/'.self::$alipayId.'/rsa_private_key.pem');
        //$sign = rsaSign($prestr, __DIR__ . '/alipay/key/rsa_private_key.pem');
        $parameter['sign'] = $sign;
        $parameter['sign_type'] = 'RSA';
        return $parameter;
    }

    // 新建退款
    public static function cqRefund(array $orders) {
        $details = "";
        $refundReason = '退款';
        $batchNum = count($orders);
        foreach ($orders as $order) {
            if (!isset($order['amount']) || !isset($order['trade_no'])) {
                return ['success' => false, 'message' => 'Invalid orders.'];
            }
            $serialsNumber = $order['trade_no'];
            $amount = $order['amount'];
            $details .= self::createRefundDetail($serialsNumber, $amount, $refundReason);
        }
        //去掉最后一个#字符
        $detailData = substr($details, 0, count($details) - 2);
        $notifyUrl = '/pay/cq-refund-notify';
        return self::postRefund($batchNum, $detailData,$notifyUrl);
    }

}
